#pragma once 

#include <iostream>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>

#include "Protocol.hpp"
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Log.hpp"

// 相互包含成员，单独声明；
class TcpServer; 
class Connection; 

typedef std::function<void(Connection*)> func_t ; 
typedef std::function<void(Connection*, std::string& request)> callback_t ; 


//一个文件描述符创建一个对象，以此对每个文件描述符进行单独的处理，以及创建单独的收/发缓冲区
class Connection
{
public:

    int _sock ; 
    // 回调方法 对应：读/写/异常
    func_t _recv_cb; 
    func_t _send_cb;
    func_t _except_cb; 
    //接收缓冲区和发送缓冲区
    std::string _inbuffer ; 
    std::string _outbuffer ; 
    // TcpServer回指指针 
    TcpServer* _tsvr;
    // 时间戳 用于记录距套接字上次发起请求的时间，如果长时间不发起请求，服务端关闭连接;
    uint64_t _lasttimestamp ; 

public:

    Connection(int sock = -1)
        :_sock(sock)
        ,_tsvr(nullptr)
    {

    }

    //错误写法 不能用初始化列表初始化  为什么 ？ 
    /*void SetCallBack(func_t recv_cb, func_t send_cb , func_t except_cb )// 什么用 ？ ？ ？ 
        :_recv_cb(recv_cb)
        ,_send_cb(send_cb)
        ,_except_cb(except_cb)
    {
        
    }*/

    //初始读/写/异常的回调方法 ？ ？ ？ 
    void SetCallBack(func_t recv_cb, func_t send_cb , func_t except_cb )
    {
        _recv_cb = recv_cb ; 
        _send_cb = send_cb ;
        _except_cb = except_cb; 
    }

    ~Connection()
    {

    }



};



//作用：1.对所有的的Connectin连接进行管理
class TcpServer
{
    //默认端口号
    const static int gport = 8080 ;
    //默认可管理最多文件描述符
    const static int gnum  = 128  ; 

private:

    int _linstensock ; 
    int _prot ; 
    Epoll _poll ; // 创建epoll模型
    
    // ? ? ? ? ? ? 
    std::unordered_map<int, Connection*> _connections ; 
    //就绪事件缓冲区   // epoll_wait把epoll模型中的就绪队列结果拷贝入_revs就绪事件缓冲区中
    epoll_event* _revs ; 
    int _revs_num ; 

    //recv在收到消息后，对消息做处理的函数。（任务处理函数） 
    callback_t _cb ; 

public:

    // 网络基础 + 多路转接模型创建 + 绑定Connection和TcpServer类 + 就绪事件缓冲区
    TcpServer(int port = gport)
        :_prot(port)
        ,_revs_num(gnum)
    {
        // 创建listensock
        _linstensock = Sock::Socket();
        Sock::Bind(_linstensock, _prot);
        Sock::Listen(_linstensock);

        // 多路转接 epoll 创建
        _poll.EpollCreate();

        // 以套接字为_linstensock为核心创建对应的Connection，并绑定Connection和TcpServer类对象
        //注意bind的使用方法，bind类内成员函数要传类对象，这里传this指针
        //std::bind(&TcpServer::Acceptrer, this, std::placeholders::_1) 本质返回值是函数指针
        AddConnection(_linstensock,  std::bind(&TcpServer::Acceptrer, this, std::placeholders::_1)  , nullptr, nullptr);

        //就绪事件缓冲区
        _revs = new epoll_event[_revs_num]; 
    }

    // 以套接字为核心创建对应的Connection并绑定Connection和TcpServer对象
    void AddConnection(int sock, func_t recv_cb, func_t send_cb , func_t except_cb )
    {
        Sock::SetNonBlock(sock); //将文件描述符设置为非阻塞状态
        //创建connection对象
        Connection* conn = new Connection(sock);
        //初始化对象的回调方法
        conn->SetCallBack(recv_cb, send_cb, except_cb);
        //绑定Connection和TcpServer对象
        conn->_tsvr = this ;
        //将sock添加到epoll模型之中并设置期望事件
        //EPOLLIN：表示对应的文件描述符可以进行读操作（可读事件）。
        //EPOLLET：表示使用边缘触发模式（边缘触发事件）。
        _poll.AddSockToEpoll(sock, EPOLLIN | EPOLLET) ; 
        //添加入Connections映射表中
        _connections.insert(std::make_pair(sock, conn));
    }


    //传入的一般来说是_linstensock的connection
    //加入新网络的连接
    void Acceptrer(Connection* conn)
    {
        while (true)
        {
            //输出型参数
            std::string clientip;
            uint16_t clientport;
            int accept_errno = 0; 
            
            int sock =  Sock::Accept(conn->_sock, &clientip, &clientport, &accept_errno) ;
            
            //必需设置输出accept_errno错误码的原因，无法判断是因为缓冲区内无内容（读完了）错误
            //还是因为其他错误，不同的错误处理方式不一样。
            //EAGAIN或 EWOULDBLOCK：表示当前没有新的连接请求，可以在稍后再次尝试。
            //EINTR：表示在接受连接的过程中被中断，可以重新尝试。
            if(sock < 0 )
            {
                if(accept_errno == EAGAIN || accept_errno == EWOULDBLOCK)
                {
                    break;
                }
                else if(accept_errno == EINTR)
                {
                    continue;
                }
                else
                {
                    //其他原因 
                    logMessage(DEBUG, "accept error, %d : %s", errno , strerror(errno));
                    break; 
                }
            }

            // 用接收到的sock生成connection对象并将其绑定入TcpServer中
            if(sock >= 0)
            {
                AddConnection(sock, 
                std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                std::bind(&TcpServer::Excepter, this, std::placeholders::_1)
                ) ; 
                logMessage(DEBUG, "accept client %s : %d success , ad to TcpServer success, sock %d ", 
                clientip.c_str(), clientport, sock); 
            }
        }
    }

    //设置文件描述符关心事件 readable为真则设置读事件关心，writeable也是；
    void EnableReadWrite(Connection* conn, bool readable, bool writeable)
    {
        uint32_t events = 0 ; 
        if(readable == true)
        {
            events = events | EPOLLIN ; 
        }
        if(writeable == true)
        {
            events = events | EPOLLOUT ; 
        }
        bool res = _poll.CtrlEpoll(conn->_sock, events);
        assert(res);
    }



    void Recver(Connection* conn)
    {
        const int num = 1024 ; 
        bool err = false ; 

        while (true)
        {
            char buffer[num] ; 
            ssize_t n = recv(conn->_sock, buffer, sizeof(buffer) - 1 , 0);
            if(n < 0)
            {
                //读取失败，检查原因
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break; 
                }
                else if(errno == EINTR)
                {
                    continue; 
                }
                else
                {
                    logMessage(ERROR, "recv error %d : %s ", errno, strerror(errno));
                    conn->_except_cb(conn);
                    err = true ; 
                    break; 
                }
            }
            else if(n == 0)
            {
                //对端退出
                logMessage(DEBUG, "client[%d] quit , server quit too [%d]", conn->_sock, conn->_sock);
                conn->_except_cb(conn);
                err = true ; 
                break; 
            }
            else
            {
                //读取成功
                buffer[n] = 0 ;
                conn->_inbuffer += buffer ;
            }
        }
        //打印该文件描述符接收后输入缓冲区内的内容
        logMessage(DEBUG, "conn->_inbuffer[sock: %d] : %s", conn->_sock, conn->_inbuffer.c_str());

        //如果没出错
        if(!err) 
        {
            //未完待续。。。。。
            std::vector<std::string> messages; 
            // 前面rec到的都是以报文格式封装的数据，但报文有可能是不完整的
            //报文解析函数：将rec的内容粘连报文 分割 解析 后放入messas 中，成为一个个独立请求
            myself::SpliteMessage(conn->_inbuffer, &messages);
            //将message中的请求添加到任务队列 
            for(auto& msg : messages)
            {
                _cb(conn, msg);
            }
        }
        
    }


    void Sender(Connection* conn)
    {
        while(true)//
        {
            ssize_t n = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
            if(n > 0)
            {
                conn->_outbuffer.erase(0,n); 
                if(conn->_outbuffer.empty())
                {   // 发完
                    break; 
                }
            }
            else // 发送失败
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK )
                {
                    break; 
                }
                else if(errno == EAGAIN)
                {
                    continue;
                }
                else
                {
                    logMessage(ERROR, "send error %d : %s", errno , strerror(errno));
                    conn->_except_cb(conn);
                    break;
                }
            }
        }
    }



    //异常事件处理
    void Excepter(Connection* conn)
    {
       if(IsConnectionsExists(conn->_sock) == false)
       {
            return ; 
       }

       bool res = _poll.DelFromEpoll(conn->_sock);
       assert(res);
       //从用户映射coontions中移除
       _connections.erase(conn->_sock);
       close(conn->_sock);

       logMessage(DEBUG, " Excepetr sock  回收" );
    }

    void LoopOnce()
    {
        int n = _poll.WaitEpoll(_revs, _revs_num);
        for(int i = 0 ; i < n ; i++)
        {
            int sock = _revs[i].data.fd ; 
            uint32_t events = _revs[i].events ; 

            //两种异常均交由read或write(write或read最终都会调用excepter)
            //EPOLLERR：表示发生错误（错误事件）。  
            //EPOLLHUP：表示发生挂起事件（挂起事件）
            if(events & EPOLLERR) 
            {
                events |= (EPOLLIN | EPOLLOUT);
            }
            if(events & EPOLLHUP) 
            {
                events |= (EPOLLIN | EPOLLOUT);
            }

            if(events & EPOLLIN)
            {
                //sock的connection是在accept中被创建并添加入tcpserver的connections中的，同时也bind了回调方法
                //那么这个if检查是为了防止什么意外情况发生呢？？？
                if(IsConnectionsExists(sock) && (_connections[sock]->_send_cb != nullptr))
                {
                    _connections[sock]->_recv_cb(_connections[sock]);
                }
            }

            if(events & EPOLLOUT)
            {
                //sock的connection是在accept中被创建并添加入tcpserver的connections中的，同时也bind了回调方法
                //那么这个if检查是为了防止什么意外情况发生呢？？？
                if(IsConnectionsExists(sock) && (_connections[sock]->_send_cb != nullptr))
                {
                    _connections[sock]->_send_cb(_connections[sock]);
                }
            }

        }
    }


    //？？？？？
    void Dispather(callback_t cb)
    {
        _cb = cb ;
        while (true)
        {
            ConnectionAliveCheck();
            LoopOnce();
        }
        
    }


    //判断连接是否超时未发送事件
    bool ConnectionAliveCheck()
    {
   
    }

    //判断连接在connections中是否存在
    //辅助判断连接是否建立成功/有效 ？ ？ ？ ？
    bool IsConnectionsExists(int sock)
    {
        auto iter = _connections.find(sock);
        if(iter == _connections.end())
        {
            return false ; 
        }
        else
        {
            return true ; 
        }
    }

    ~TcpServer()
    {

    }


};